package club.kingyin.kyblogprocore.controller;

import club.kingyin.kyblogprocore.core.classifier.DocumentClassifier;
import club.kingyin.commons.clients.SearchClient;
import club.kingyin.commons.clients.UserClient;
import club.kingyin.commons.constant.AuthorizeConstant;
import club.kingyin.commons.entity.Ids;
import club.kingyin.commons.entity.core.Comment;
import club.kingyin.commons.entity.core.Document;
import club.kingyin.commons.enums.BlogEnum;
import club.kingyin.commons.response.Result;
import club.kingyin.easycache.annotation.EasyCache;
import club.kingyin.easycache.core.impl.AbstractEasyCacheKey;
import club.kingyin.easycache.core.impl.DefaultEasyCache;
import club.kingyin.kyblogprocore.core.cache.DocumentIdRemoveHandler;
import club.kingyin.kyblogprocore.core.cache.ElasticSearchAsyncHandler;
import club.kingyin.kyblogprocore.core.cache.ViewCountEasyCacheHandler;
import club.kingyin.kyblogprocore.core.hot.DocumentHot;
import club.kingyin.kyblogprocore.dao.CommentDao;
import club.kingyin.kyblogprocore.dao.DocumentDao;
import club.kingyin.kyblogprocore.dao.TagDao;
import club.kingyin.kyblogprocore.entity.DocumentPram;
import club.kingyin.commons.entity.core.Documents;
import club.kingyin.kyblogprocore.service.TagService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import club.kingyin.kyblogprocore.service.DocumentService;
import lombok.SneakyThrows;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * (Document)表控制层
 *
 * @author makejava
 * @since 2022-02-16 18:22:14
 */
@RestController
@RequestMapping("document")
public class DocumentController {
    /**
     * 服务对象
     */
    @Resource
    private DocumentService documentService;

    @Resource
    private DocumentDao documentDao;

    @Resource
    private CommentDao commentDao;

    @Resource
    private SearchClient searchClient;

    @Resource
    private UserClient userClient;

    @Resource
    private TagService tagService;

    @Resource
    private DefaultEasyCache defaultEasyCache;

    @Resource
    private DocumentClassifier documentClassifier;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 分页查询所有数据
     *
     * @param prams 分页对象
     * @return 所有数据
     */
    @PostMapping("/all")
    public Result selectAll(@RequestHeader(AuthorizeConstant.ROLE) String role,
                            @RequestBody DocumentPram prams) throws UnsupportedEncodingException {
        switch (Objects.requireNonNull(BlogEnum.deBlogEnum(URLDecoder.decode(role, "UTF8")))) {
            case ADMIN:
            case ROOT:
                return Result.success(this.documentService.page(prams.getPage(), new LambdaQueryWrapper<>(prams.getDocument()).select(Document::getTitle,Document::getId,Document::getStatus,Document::getViews,Document::getUserid,Document::getBackground,Document::getSlogan,Document::getUpdatetime).orderByDesc(Document::getUpdatetime)));
            default: return Result.failR("无操作权限");
        }
    }

    @GetMapping("/edit")
    public Result getDetail(@RequestHeader(AuthorizeConstant.ID) int userid, int id) {
        return Result.success(documentService.getDocumentByUserIdAndId(userid, id));
    }

    @EasyCache(prams = {"userid", "page=pram.page.current","size=pram.page.size","pram.document.title","pram.document.tag","pram.document.status"}, module = "ky-core")
    @PostMapping("/list")
    public Result documentPage(@RequestHeader(AuthorizeConstant.ID) int userid, @RequestBody DocumentPram pram) {
        pram.getDocument().setUserid(userid);
        return Result.success(documentService.selectDocumentPage(pram.getPage(), pram.getDocument()));
    }

    @EasyCache(prams = {"userid=pram.document.userid","page=pram.page.current","size=pram.page.size","tag=pram.document.tag"}, module = "ky-core")
    @PostMapping("/articles")
    public Result articles(@RequestBody DocumentPram pram) {
        if ("0-".equals(pram.getDocument().getTag())) {
            pram.getDocument().setTag(null);
        }
        return Result.success(documentService.selectDocuments(pram.getPage(), pram.getDocument()));
    }

    @GetMapping("/home")
    public Result home(@RequestParam("current") int current) {
        Page<Document> home = documentService.selectHome(current);
        if (home.getSize()<21) {
            home = documentService.selectHome(1);
        }
        return Result.success(home);
    }

    @GetMapping("/bottom")
    public Result bottom(@RequestParam("current") int current) {
        Page<Document> bottom = documentService.selectBottom(current);
        if (bottom.getSize()<15) {
            bottom = documentService.selectBottom(1);
        }
        return Result.success(bottom);
    }

    @EasyCache(module = "ky-core")
    @PostMapping("/ids")
    public Result getDocsByIds(@RequestBody Documents ids) {
        LambdaQueryWrapper<Document> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(Document::getId,ids.getIds()).select(Document::getId,Document::getSlogan,Document::getTitle,Document::getBackground,Document::getUpdatetime,Document::getViews,Document::getClassify);
        return Result.success(documentDao.selectList(queryWrapper));
    }

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @EasyCache(prams = {"id"}, module = "ky-core", handler = {ViewCountEasyCacheHandler.class})
    @GetMapping("/detail")
    public Result selectOne(@RequestHeader(AuthorizeConstant.ID) int userid,@RequestParam("id") int id) {
        LambdaQueryWrapper<Document> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Document::getId,id).eq(Document::getStatus,BlogEnum.RELEASE);
        Document document = this.documentService.getById(id);
        if (document != null && document.getStatus() == BlogEnum.RELEASE) {
            rabbitTemplate.convertAndSend("recommend.queue",userid);
        }
        if (document == null) {
            document = new Document();
            document.setTitle("文章不存在");
            document.setSlogan("禁止访问无效的文章");
            document.setBackground("http://dev.kingyin.club:9000/cloud/2022225/背景图片/2021_9_9_22_1646715064217.jpg");
            document.setContext(Base64.getEncoder().encodeToString("文章不存在".getBytes(StandardCharsets.UTF_8)));
        }
        else if (document.getStatus() != BlogEnum.RELEASE) {
            // 文章替换
                document.setTitle("Ky-Blog 当前标题不可见 ID："+document.getId());
                document.setSlogan("Ky-Blog 当前简介不可见 请等待发布");
                document.setBackground("http://dev.kingyin.club:9000/cloud/2022225/背景图片/2021_9_9_22_1646715064217.jpg");
                document.setContext(Base64.getEncoder().encodeToString("文章暂未发布".getBytes(StandardCharsets.UTF_8)));
        }
        return Result.success(document);
    }

    @GetMapping("/hot")
    public Result hot() {
        return Result.success(DocumentHot.getHots());
    }

    @GetMapping("/doc/detail")
    public Result getTagsAngCommentCount(@RequestParam("id") int id) {
        LambdaQueryWrapper<Document> doc = Wrappers.lambdaQuery();
        doc.eq(Document::getId,id).select(Document::getTag,Document::getTag,Document::getUserid);
        Document document = documentDao.selectOne(doc);

        Map<String,Object> res = new HashMap<>();
        if (document == null) {
            res.put("tags", new String[]{"未找到"});
            res.put("comments",0);
            return Result.success(res);
        }

        List<String> tags = new ArrayList<>();
        if (!document.getTag().isEmpty()) {
            tags = tagService.getTagsByTag(document.getTag());
        }
        QueryWrapper<Comment> comment = Wrappers.query();
        comment.select("sum(size+1) as size").eq("objectid",id).isNull("parentid");
        Comment size = commentDao.selectOne(comment);
        res.put("tags",tags);
        res.put("comments",size==null?0:size.getSize());
        return Result.success(res);
    }
//    /**
//     * 新增数据
//     *
//     * @param document 实体对象
//     * @return 新增结果
//     */
//    @PostMapping
//    public Result insert(@RequestBody Document document) {
//        return Result.success(this.documentService.save(document));
//    }

    @PostMapping("/classifier")
    public Result classifier(@RequestBody Document document) {
        String words = new String(Base64.getDecoder().decode(document.getContext()), StandardCharsets.UTF_8);
        Map<String, Object> map = new HashMap<>();
        map.put("all",documentClassifier.getAllClassifier());
        List<String> classifier = documentClassifier.getClassifier(document.getTitle() + "。" + document.getSlogan() + "。" + words);
        map.put("classifier",classifier);
        return Result.success(map);
    }

    @EasyCache(prams = {"id=document.id"}, handler = {DocumentIdRemoveHandler.class, ElasticSearchAsyncHandler.class})
    @EasyCache(prams = {"userid"}, delete = {"documentPage","infos","articles"}, module = "ky-core")
    @EasyCache(prams = {"id=document.id"}, delete = {"selectOne"}, module = "ky-core")
    @PostMapping("/save")
    public Result save(@RequestHeader(AuthorizeConstant.ID) int userid, @RequestBody Document document) {
        document.setUserid(userid);
        long timestamp = System.currentTimeMillis();
        if (document.getStatus() != BlogEnum.DRAFT) {
            if (document.getId() == 0) {
                document.setTimestamp(timestamp);
            }
        } else {
            document.setTimestamp(0L);
        }
        document.setUpdatetime(timestamp);
        if (document.getId() != 0) {

            if (documentDao.selectById(document.getId()).getTimestamp().equals("0")) {
                document.setTimestamp(timestamp);
            }
            // 去修改
            return documentService.updateById(document)?Result.infoR("更新成功"):Result.failR("更新失败");
        } else {
            // 判断是否有相同标题的文章
            int i = documentService.containsIdByTitleAndUserId(document.getTitle(), document.getUserid());
            if (i != -1) {
                // 更新
                document.setId(i);
                return documentService.updateById(document)?Result.infoR("更新成功"):Result.failR("更新失败");
            }
        }
        document.setViews(0L);
        return documentService.save(document)?Result.infoN(document.getStatus() == BlogEnum.DRAFT?"已保存":"已发布"):Result.failR("保存失败");
    }

    @GetMapping("/search")
    public Result searchByTitle(@RequestHeader(AuthorizeConstant.ID) int userid, @RequestParam("title") String title) {
        return Result.success(documentService.searchIdByTitleAndUserId(title, userid));
    }

    // delete参数是关联的缓存方法名，prams是共有的参数，这里会删除documentPage中userid和当前方法userid相同的缓存
    @EasyCache(prams = "id", handler = ElasticSearchAsyncHandler.class)
    @EasyCache(prams = {"userid"}, delete = {"documentPage","infos","articles"}, module = "ky-core")
    @GetMapping("/delete")
    public Result deleteDocument(@RequestHeader(AuthorizeConstant.ID) int userid,
                                 @RequestHeader(AuthorizeConstant.ROLE) String role,
                                 @RequestParam("id") int id) throws UnsupportedEncodingException {
        // 按照权限删除
        LambdaQueryWrapper<Document> queryWrapper = Wrappers.lambdaQuery();
        switch (Objects.requireNonNull(BlogEnum.deBlogEnum(URLDecoder.decode(role, "UTF8")))) {
                case ADMIN:
                case ROOT:
                    queryWrapper.eq(Document::getId,id);
                    break;
                default:
                    queryWrapper.eq(Document::getUserid,userid).eq(Document::getId,id);
        }
        return documentService.remove(queryWrapper)?Result.infoN("删除成功"):Result.failR("删除失败");
    }

    @EasyCache(prams = {"id","status","userid"}, handler = {ElasticSearchAsyncHandler.class})
    @EasyCache(prams = {"userid"}, delete = {"documentPage","articles"}, module = "ky-core")
    @EasyCache(prams = {"id"}, delete = {"selectOne"}, module = "ky-core")
    @GetMapping("/status")
    public Result updateStatus(@RequestHeader(AuthorizeConstant.ID) int userid,
                               @RequestHeader(AuthorizeConstant.ROLE) String role,
                               @RequestParam("id") int id, @RequestParam("status") BlogEnum status) throws UnsupportedEncodingException {
        // 系统更新
        if (status == BlogEnum.UNDERFRAME) {
            switch (Objects.requireNonNull(BlogEnum.deBlogEnum(URLDecoder.decode(role, "UTF8")))) {
                case ADMIN:
                case ROOT:
                    status = BlogEnum.DRAFT;
                    Document document = new Document();
                    document.setStatus(status);
                    document.setId(id);
                    // 这是必要的 删除缓存
                    defaultEasyCache.remove(new AbstractEasyCacheKey() {
                        @Override
                        protected void setPrams() {
                            prams.put("userid", documentDao.selectOne(new LambdaQueryWrapper<Document>().eq(Document::getId,id).select(Document::getUserid)).getUserid());
                        }
                    },"ky-core","documentPage","articles");
                    return documentService.updateById(document)?Result.infoN("已下架"):Result.infoR("下架失败");
                default: return Result.failR("无操作权限");
            }
        }
        // 用户更新
        LambdaUpdateWrapper<Document> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.set(Document::getStatus,status).eq(Document::getUserid,userid).eq(Document::getId,id);
        if (status != BlogEnum.DRAFT) {
            if (documentDao.selectById(id).getTimestamp().equals("0")) {
                updateWrapper.set(Document::getTimestamp,System.currentTimeMillis());
            }
        }
        return documentService.update(updateWrapper)?Result.infoN("已更新"):Result.failR("更新失败");
    }

//    /**
//     * 修改数据
//     *
//     * @param document 实体对象
//     * @return 修改结果
//     */
//    @PutMapping
//    public Result update(@RequestBody Document document) {
//        return Result.success(this.documentService.updateById(document));
//    }

//    /**
//     * 删除数据
//     *
//     * @param idList 主键结合
//     * @return 删除结果
//     */
//    @DeleteMapping
//    public Result delete(@RequestParam("idList") List<Long> idList) {
//        return Result.success(this.documentService.removeByIds(idList));
//    }

    @GetMapping("/views/{id}")
    public Result views(@PathVariable Integer id) {
        // 返回访问量
        return Result.success(documentService.getViewsById(id));
    }
    @GetMapping("/count")
    public Result getDocumentAndViews(@RequestHeader(AuthorizeConstant.ID) int userid) {
        LambdaQueryWrapper<Document> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper
                .eq(Document::getUserid,userid)
                .select(Document::getTitle,Document::getViews).orderByDesc(Document::getViews).last("limit 10");
        return Result.success(documentDao.selectList(queryWrapper));
    }

    @GetMapping("/title")
    public Result randomOneTitle() {
        return Result.success(documentDao.randomOneTitle());
    }

    @PostMapping("/async")
    public Result asyncDocument(@RequestBody Documents ids) {
        if (ids == null || ids.getIds() == null || ids.getIds().length == 0) {
            return Result.infoR("参数错误");
        }
        return doAsync(Arrays.asList(ids.getIds().clone()));
    }

    private Result doAsync(List<Integer> ids) {
        Set<Integer> success = new HashSet<>();
        Set<Integer> failed = new HashSet<>();
        Map<String,Set<Integer>> res = new HashMap<>();
        res.put("success",success);
        res.put("failed",failed);
        Result result = Result.success(res);
        for (Document save : documentDao.selectBatchIds(ids)) {
            save.setTag(JSON.toJSONString(tagService.getTagsByTag(save.getTag())));
            save.setContext(new String(Base64.getDecoder().decode(save.getContext()), StandardCharsets.UTF_8));
            if (searchClient.save(save).getData() == null) {
                failed.add(save.getId());
            } else {
                success.add(save.getId());
            }
        }
        if (success.size()>0) {
            Ids mIds = new Ids();
            Integer[] integers = new Integer[success.size()];
            int idx = 0;
            for (Integer integer : success) {
                integers[idx++] = integer;
            }
            mIds.setIds(integers);
            userClient.removeByAsyncFailedIds(mIds);
        }

        return result;
    }

    @SneakyThrows
    @GetMapping("/async/all")
    public Result asyncAllDocument(@RequestHeader(AuthorizeConstant.ROLE) String role) {
        switch (Objects.requireNonNull(BlogEnum.deBlogEnum(URLDecoder.decode(role, "UTF8")))) {
            case ADMIN:
            case ROOT:
                return doAsync(documentService.getIds());
            default: return Result.failR("无操作权限");
        }
    }
}

